﻿
using CSharpLibs.Annotations;
using System.Collections;
using System.Collections.Generic;

namespace System
{
	[Builtin]
	public class String : Object, IComparable, IComparable<String>, IEquatable<String>, IEnumerable<char>, IEnumerable
	{
		private char[] sval;

		private int hashcode;
		
		public static string Empty;
		private static string[] string_intern;

		static String()
		{
			char[] empty = new char[0];
			Empty = new String(empty);

			string_intern = new string[10];
			for(int i = 0; i < string_intern.Length; ++i)
				string_intern[i] = null;
		}

		public String(char[] chars)
			: this(chars, 0, chars.Length)
		{ ;}

		public String(char[] chars, int start, int length)
		{
			this.sval = new char[length];
			this.hashcode = 0;

			for(int i = 0; i < length; ++i)
			{
				hashcode ^= chars[start + i];
				hashcode = hashcode << 4;

				this.sval[i] = chars[start + i];
			}
		}

		public static int Compare(string s1, string s2)
		{
			if(s1 == null)
			{
				if(s2 == null)
					return 0;
				else
					return -1;
			}
			else if(s2 == null)
				return 1;
			else
			{
				if(s1.Length < s2.Length)
					return -1;
				else if(s1.Length > s2.Length)
					return 1;
				else
				{
					int len = s1.Length;
					for(int i = 0; i < len; ++i)
					{
						if(s1[i] != s2[i])
						{
							if(s1[i] < s2[i])
								return -1;
							else
								return 1;
						}
					}

					return 0;
				}
			}
		}

		public int CompareTo(object o)
		{
			if(o == null)
				return 1;

			if(!(o is String))
				Environment.Exit(1);
			
			return Compare(this, (string)o);
		}

		public int CompareTo(string str)
		{ return Compare(this, str); }

		public static string Concat(string s1, string s2)
		{
			String[] sa = new String[2];
			sa[0] = s1;
			sa[1] = s2;

			return Concat(sa); 
		}

		public static string Concat(string s1, string s2, string s3)
		{
			string[] sa = new string[3];
			sa[0] = s1;
			sa[1] = s2;
			sa[2] = s3;

			return Concat(sa);
		}

		public static string Concat(string s1, string s2, string s3, string s4)
		{
			string[] sa = new string[4];
			sa[0] = s1;
			sa[1] = s2;
			sa[2] = s3;
			sa[3] = s4;

			return Concat(sa);
		}

		public static string Concat(string[] sa1)
		{
			int slen = 0;
			for(int i = 0; i < sa1.Length; ++i)
				slen += sa1[i].Length;

			char[] res = new char[slen];
			int pos = 0;
			for(int i = 0; i < sa1.Length; ++i)
			{
				for(int j = 0; j < sa1[i].Length; ++j)
				{
					res[pos] = sa1[i][j];
					++pos;
				}
			}

			return new String(res);
		}

		public static string Concat(object o1)
		{
			object[] obs = new object[1];
			obs[0] = o1;

			return String.Concat(obs);
		}

		public static string Concat(object o1, object o2)
		{
			object[] obs = new object[2];
			obs[0] = o1;
			obs[1] = o2;

			return String.Concat(obs);
		}

		public static string Concat(object o1, object o2, object o3)
		{
			object[] obs = new object[3];
			obs[0] = o1;
			obs[1] = o2;
			obs[2] = o3;

			return String.Concat(obs);
		}

		public static string Concat(object[] sa1)
		{
			string[] sa = new string[sa1.Length];
			for(int i = 0; i < sa1.Length; ++i)
				sa[i] = (sa1[i] == null ? String.Empty : sa1[i].ToString());

			return Concat(sa);
		}

		public void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count)
		{
			for(int i = sourceIndex; i < count; ++i)
				destination[destinationIndex + i] = this[i];
		}

		public bool EndsWith(string s)
		{
			if(this.Length < s.Length)
				return false;
			else
			{
				int bpos = this.Length - s.Length;
				for(int i = 0; i < s.Length; ++i)
				{
					if(s[i] != this[bpos + i])
						return false;
				}

				return true;
			}
		}

		public override bool Equals(object o)
		{ return this.CompareTo(o) == 0; }

		public bool Equals(string s)
		{ return Compare(this, s) == 0; }

		public static string Format(string fms, object o1)
		{
			object[] obs = new object[1];
			obs[0] = o1;

			return String.Format(fms, obs); 
		}

		public static string Format(string fms, object o1, object o2)
		{
			object[] obs = new object[2];
			obs[0] = o1;
			obs[1] = o2;

			return String.Format(fms, obs);
		}

		public static string Format(string fms, object o1, object o2, object o3)
		{
			object[] obs = new object[3];
			obs[0] = o1;
			obs[1] = o2;
			obs[2] = o3;

			return String.Format(fms, obs);
		}

		public static string Format(string fms, object[] sa1)
		{
			string[] sa = new string[sa1.Length];
			for(int i = 0; i < sa1.Length; ++i)
				sa[i] = (sa1[i] == null ? String.Empty : sa1[i].ToString());

			return FormatInsert(fms, sa);
		}

		[BuiltinMethod, BuiltinSemantics("internal_string_formatbuild")]
		private static String FormatInsert(string fms, string[] args)
		{ return null; }

		public CharEnumerator GetEnumerator()
		{ return new CharEnumerator(this); }

		public override int GetHashCode()
		{ return this.hashcode; }

		public int IndexOf(char c)
		{ return this.IndexOf(c, 0); }

		public int IndexOf(string s)
		{ return this.IndexOf(s, 0); }

		public int IndexOf(char c, int pos)
		{
			for(int i = pos; i < this.Length; ++i)
			{
				if(this[i] == c)
					return i;
			}

			return -1;
		}

		public int IndexOf(string s, int pos)
		{
			//we use a simple Karp-Rabin search here.
			int expbase = 3;
			int modulo = 1009;

			//get modulo value of final position.
			int fmpv = 1;
			for(int i = 1; i < s.sval.Length; ++i)
				fmpv = (fmpv * expbase) % modulo;

			int hsub = HashRng(s.sval, 0, s.sval.Length, expbase, modulo);
			int hs = HashRng(this.sval, pos, s.sval.Length, expbase, modulo);
			for(int i = pos; i < this.sval.Length - s.sval.Length; ++i)
			{
				if(hs == hsub)
				{
					bool ok = true;
					for(int j = 0; j < s.sval.Length; ++j)
					{
						if(this.sval[i + j] != s.sval[j])
						{
							ok = false;
							break;
						}
					}

					if(ok)
						return i;
				}

				hs = UpdateHash(this.sval, hs, i, i + s.sval.Length + 1, modulo, fmpv);
			}

			return -1;
		}

		private static int HashRng(char[] s, int start, int len, int expbase, int modulo)
		{
			int hash = 0;
			for(int i = 0; i < len; ++i)
				hash = ((hash * expbase) % modulo) + s[start + i];
			return hash;
		}

		private static int UpdateHash(char[] s, int hash, int removepos, int addpos, int modulo, int maxmod)
		{
			hash = hash - ((s[removepos] * maxmod) % modulo);
			hash = hash + s[addpos];

			return hash;
		}

		public int IndexOfAny(char[] c)
		{ return IndexOfAny(c, 0); }

		public int IndexOfAny(char[] c, int pos)
		{
			for(int i = pos; i < this.Length; ++i)
			{
				for(int j = 0; j < c.Length; ++j)
				{
					if(this[i] != c[j])
						return i;
				}
			}

			return -1;
		}

		private static int InternPosFor(string s)
		{
			int pos = s.GetHashCode() % string_intern.Length;
			if(string_intern[pos] == null || s.Equals(string_intern[pos]))
				return pos;

			int i = (pos + 1) % string_intern.Length;
			while(i != pos)
			{
				if(string_intern[i] == null || s.Equals(string_intern[i]))
					return i;

				i = (i + 1) % string_intern.Length;
			}

			return -1;
		}

		public static string Intern(string s)
		{
			int pos = InternPosFor(s);

			//if the table is full rehash
			if(pos == -1)
			{
				string[] tsa = string_intern;
				string_intern = new string[tsa.Length * 2];
				for(int i = 0; i < string_intern.Length; ++i)
					string_intern[i] = null;

				for(int i = 0; i < tsa.Length; ++i)
				{
					int rpos = InternPosFor(tsa[i]);
					string_intern[rpos] = tsa[i];
				}

				//recompte pos
				pos = InternPosFor(s);
			}

			if(string_intern[pos] == null)
				string_intern[pos] = s;

			return string_intern[pos];
		}

		public static bool IsNullOrEmpty(string value)
		{
			if(value != null)
				return (value.Length == 0);
			return true;
		}

		public int LastIndexOf(char c)
		{ return this.LastIndexOf(c, this.Length - 1); }

		public int LastIndexOf(char c, int pos)
		{
			for(int i = pos; i >= 0; --i)
			{
				if(this[i] == c)
					return i;
			}

			return -1;
		}

		public static bool op_Equality(string a, string b)
		{ return Compare(a, b) == 0; }

		public static bool op_Inequality(string a, string b)
		{ return Compare(a, b) != 0; }

		public string Replace(string srepl, string swith)
		{
			int cpos = 0;
			int npos = this.IndexOf(srepl, 0);
			char[] newstr = new char[this.sval.Length];
			int newpos = 0;

			while(npos != -1)
			{
				if((newstr.Length - newpos) < ((npos - cpos) + swith.sval.Length))
				{
					char[] tmp = newstr;
					newstr = new char[tmp.Length * 2];
					for(int k = 0; k < newpos; ++k)
						newstr[k] = tmp[k];
				}

				while(cpos < npos)
				{
					newstr[newpos] = this.sval[cpos];
					++cpos;
					++newpos;
				}

				for(int j = 0; j < swith.sval.Length; ++j)
				{
					newstr[newpos] = swith.sval[j];
					++newpos;
				}

				cpos = npos + swith.sval.Length;
				npos = this.IndexOf(srepl, cpos);
			}

			while(cpos < this.sval.Length)
			{
				newstr[newpos] = this.sval[cpos];
				++cpos;
				++newpos;
			}

			return new String(newstr, 0, newpos);
		}

		public string[] Split(char[] toks)
		{ return null; }

		public bool StartsWith(string s)
		{
			if(this.Length < s.Length)
				return false;
			else
			{
				for(int i = 0; i < s.Length; ++i)
				{
					if(s[i] != this[i])
						return false;
				}

				return true;
			}
		}

		public string Substring(int start)
		{ return this.Substring(start, this.Length - start); }

		public string Substring(int start, int length)
		{
			char[] chrs = new char[length];
			for(int i = 0; i < length; ++i)
				chrs[i] = this[start + i];

			return new String(chrs);
		}

		public char[] ToCharArray()
		{
			char[] chrs = new char[this.Length];
			for(int i = 0; i < this.Length; ++i)
				chrs[i] = this[i];

			return chrs;
		}

		public string ToLower()
		{
			char[] chrs = new char[this.Length];
			for(int i = 0; i < this.Length; ++i)
				chrs[i] = Char.ToLower(this[i]);

			return new String(chrs);
		}

		public string ToUpper()
		{
			char[] chrs = new char[this.Length];
			for(int i = 0; i < this.Length; ++i)
				chrs[i] = Char.ToUpper(this[i]);

			return new String(chrs);
		}

		public string Trim()
		{
			if(this.sval.Length == 0)
				return this;

			int spos = 0;
			while(Char.IsWhiteSpace(this.sval[spos]))
				++spos;

			int epos = this.sval.Length - 1;
			while(Char.IsWhiteSpace(this.sval[epos]))
				--epos;

			return new String(this.sval, spos, epos - spos);
		}

		public override string ToString()
		{ return this; }

		public char this[int index]
		{ get { return this.sval[index]; } }

		public int Length
		{ get { return this.sval.Length; } }

		IEnumerator<char> IEnumerable<char>.GetEnumerator()
		{ return this.GetEnumerator(); }

		IEnumerator IEnumerable.GetEnumerator()
		{ return this.GetEnumerator(); }
	}
}
